/*
	process_control.c

	contains functionality for initialising and terminating system processes as well as signal handling for both

	Author: Aidan Goddard 13/12/13
*/

#include"headers/kernel_tables.h"
#include"headers/bakery_lock.h"
#include"headers/process_control.h"
#include"headers/scheduler_functions.h"
#include"headers/system_error.h"
#include"headers/page_table_control.h"
#include"../driver/ACDM/ACDM_mod.h"

#include"../klib/headers/klib_system_resources.h"

#include"../setup/headers/printf.h"

/////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////
// internal functions for TLC allocation/deallocation
// assumes interrupts are disabled

static SCH_TLC *PopTLC()
{
	// check the stack index for max value
	if(SCHEDULER->TLC_stack_index == 8192)
		return NULL;

	// stack is not empty
	// get lock on the stack
	BakeryEnter(&(SCHEDULER->TLC_stack_lock), GetCPUID());

	// get the TLC object
	SCH_TLC *to_return = SCHEDULER->TLC_stack[SCHEDULER->TLC_stack_index];
	SCHEDULER->TLC_stack_index++;

	// release lock
	BakeryLeave(&(SCHEDULER->TLC_stack_lock), GetCPUID());

	// return it
	return to_return;
}

void PushTLC(SCH_TLC *tlc)
{
	// check the stack isn't full
	if(SCHEDULER->TLC_stack_index == 32)
		return;

	// stack is not full
	// get lock on the stack
	BakeryEnter(&(SCHEDULER->TLC_stack_lock), GetCPUID());

	// get the TLC object
	SCHEDULER->TLC_stack_index--;
	SCHEDULER->TLC_stack[SCHEDULER->TLC_stack_index] = tlc;

	// release lock
	BakeryLeave(&(SCHEDULER->TLC_stack_lock), GetCPUID());

	// return
	return;
}


/////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////
// internal functions for PCB allocation/deallocation
// assumes interrupts are disabled
static SCH_PCB *PopPCB(uint32_t *PCB_number)
{
	// check the stack index for max value
	if(SCHEDULER->PCB_stack_index == 2048)
	{
		return NULL;
	}

	// stack is not empty, get lock on stack
	BakeryEnter(&(SCHEDULER->PCB_stack_lock), GetCPUID());

	// get the PCB object
	uint32_t PCB_num = SCHEDULER->PCB_stack[SCHEDULER->PCB_stack_index];
	*PCB_number = PCB_num;
	SCH_PCB *to_return = (SCH_PCB*)(uint64_t)((PCB_num * sizeof(SCH_PCB)) + (uint64_t)SCHEDULER);
	SCHEDULER->PCB_stack_index++;

	// increase system process count
	SCHEDULER->total_process_count++;

	// release lock
	BakeryLeave(&(SCHEDULER->PCB_stack_lock), GetCPUID());

	// return it
	return to_return;
}

static void PushPCB(SCH_PCB *pcb)
{
	// check if the stack isn't full (shouldn't be)
	if(SCHEDULER->PCB_stack_index == 16)
	{
		KERNEL_ERROR("kernel/process_control", "push_pcb", "pushing pcb when stack already full");
		return;
	}

	// stack is not empty, get lock on stack
	BakeryEnter(&(SCHEDULER->PCB_stack_lock), GetCPUID());

	// return the PCB object
	uint32_t PCB_num = (uint32_t)((((uint64_t)pcb) - ((uint64_t)SCHEDULER)) / sizeof(SCH_PCB));
	SCHEDULER->PCB_stack_index--;
	SCHEDULER->PCB_stack[SCHEDULER->PCB_stack_index] = PCB_num;

	// decrease system process count
	SCHEDULER->total_process_count--;

	// release lock
	BakeryLeave(&(SCHEDULER->PCB_stack_lock), GetCPUID());

	// finished
	return;
}

// internal function for finding the next available system PCB
static SCH_CLS system_pcb_lock;
static SCH_PCB *GetSystemPCB(uint32_t *PCB_number)
{
	// define a value to return. will remain null if nothing is available
	SCH_PCB *to_return = NULL;

	// get a lock on the list
	BakeryEnter(&(system_pcb_lock), GetCPUID());

	// search through the list for a PID of zero (exclude the idle process)
	int i;
	for(i = 1; i < 16; i++)
	{
		// get the PID
		uint64_t PID = SCHEDULER->PCB[i].PID;

		// check it for zero
		if(PID == 0)
		{
			// set the return value to that of the current PCB
			SCHEDULER->PCB[i].PID = i;	// temp PID to prevent multiple allocations
			to_return = &(SCHEDULER->PCB[i]);
			*PCB_number = i;
			break;
		}
	}

	// release the lock
	BakeryLeave(&(system_pcb_lock), GetCPUID());

	// finished
	return to_return;
}

// internal function for releasing a system PCB
static void ReleaseSystemPCB(SCH_PCB *pcb)
{
	// get a lock on the PCB
	BakeryEnter(&(pcb->lock), GetCPUID());
	BakeryEnter(&(system_pcb_lock), GetCPUID());

	// mark the PID as zero to show it is available
	pcb->PID = 0;

	// release lock
	BakeryLeave(&(system_pcb_lock), GetCPUID());
	BakeryLeave(&(pcb->lock), GetCPUID());

	// finished
	return;
}


/////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////

// internal functions to spawn the master thread for the process
// assumes interrupts are disabled
// assumes in memory space of new process
// assumes PCB and PDB are already set up when called
// do not need to lock the PCB as no one else will be using it
static SCH_CLS thread_count_lock;
static void SpawnMasterThread(uint64_t PID, uint64_t(*start_address)(void), void *stack_ptr, uint8_t CS, uint64_t param1, uint64_t param2)
{
	// attempt to allocate a thread container
	SCH_TLC *tlc = PopTLC();
	if(tlc == NULL)
	{
		// system error here - out of thread containers
		KERNEL_ERROR("kernel/process_control", "spawn_master_thread", "no more thread containers to allocate to new master thread");
		return;
	}

	// get the PCB index
	uint32_t PCB = PID & 0xffff;

	// TID
	uint64_t TID = CreateTID(PID, 0);

	// fill the TLC structure
	tlc->PCB_index = PCB;
	tlc->TCB_index = 0;

	// create the thread
	// use process local thread 0
	// get the RFLAGS value
	uint64_t RFLAGS = 0;
	__asm__ __volatile__("pushfq; pop %%rax; movq %%rax, %0;":"=r" (RFLAGS)::"memory", "%rax");

	// set defaults on flags (enable interrupts, IOPL (?))
	RFLAGS = RFLAGS | (1 << 9);	// IF flag (Interrupts Enabled)

	// set the initial values
	uint64_t fpu_address = (uint64_t)SCHEDULER->PCB[PCB].TCB[0].FPU;
	__asm__ __volatile__("fxsave (%%rax)"::"a" (fpu_address));
	SCHEDULER->PCB[PCB].TCB[0].CS = CS;
	SCHEDULER->PCB[PCB].TCB[0].RIP = (uint64_t)start_address;
	SCHEDULER->PCB[PCB].TCB[0].RSP = (uint64_t)stack_ptr;
	SCHEDULER->PCB[PCB].TCB[0].RFLAGS = RFLAGS;
	SCHEDULER->PCB[PCB].TCB[0].RAX = 0;
	SCHEDULER->PCB[PCB].TCB[0].RBX = 0;
	SCHEDULER->PCB[PCB].TCB[0].RCX = 0;
	SCHEDULER->PCB[PCB].TCB[0].RDX = 0;
	SCHEDULER->PCB[PCB].TCB[0].RBP = 0;
	SCHEDULER->PCB[PCB].TCB[0].R8 = 0;
	SCHEDULER->PCB[PCB].TCB[0].R9 = 0;
	SCHEDULER->PCB[PCB].TCB[0].R10 = 0;
	SCHEDULER->PCB[PCB].TCB[0].R11 = 0;
	SCHEDULER->PCB[PCB].TCB[0].R12 = 0;
	SCHEDULER->PCB[PCB].TCB[0].R13 = 0;
	SCHEDULER->PCB[PCB].TCB[0].R14 = 0;
	SCHEDULER->PCB[PCB].TCB[0].R15 = 0;
	SCHEDULER->PCB[PCB].TCB[0].TID = TID;

	// set the first 2 parameters for the entry function
	SCHEDULER->PCB[PCB].TCB[0].RDI = param1;
	SCHEDULER->PCB[PCB].TCB[0].RSI = param2;

	// set the thread state to active
	SCHEDULER->PCB[PCB].PDB->thread_usage_map[0] = THREAD_STATE_ACTIVE;

	// update the active thread count
	SCHEDULER->PCB[PCB].active_threads = 1;

	// thread is created
	// increase system thread count
	BakeryEnter(&(thread_count_lock), GetCPUID());
	SCHEDULER->total_thread_count++;
	BakeryLeave(&(thread_count_lock), GetCPUID());

	// find a CPU to move it to
	int CPU = GetCPUWithSmallestActiveQueue();

	// move it to that CPU
	MoveThreadToCPU(CPU, tlc);

	// finished, return
	return;
}


/////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////

// function to spawn a new thread for the current process
// assumes interrupts are disabled
uint64_t SpawnSlaveThread(uint64_t(*start_address)(void*), void *data, void *stack_ptr)
{
	// attempt to grab a TLC for the thread
	// return 0 if no more TLCs available (unlikely!)
	SCH_TLC *tlc = PopTLC();
	if(tlc == NULL)
		return 0;

	// get the Pindex for this process
	int Pindex = SCHEDULER->CPU_current_thread[GetCPUID()]->PCB_index;

	// lock the PCB
	BakeryEnter(&(SCHEDULER->PCB[Pindex].lock), GetCPUID());

	// check if the process can have any more threads
	int max_threads = SCHEDULER->PCB[Pindex].max_threads_in_TCB;
	SCH_PDB *pdb = SCHEDULER->PCB[Pindex].PDB;
	int new_Tindex = 0;
	int i;
	for(i = 0; i < max_threads; i++)
	{
		if(pdb->thread_usage_map[i] == THREAD_STATE_INACTIVE)
		{
			new_Tindex = i;
		}
	}

	// check if Tindex is valid (non-zero)
	if(new_Tindex == 0)
	{
		// can't allocate thread space
		PushTLC(tlc);
		return 0;
	}

	// fill the tlc
	tlc->PCB_index = Pindex;
	tlc->TCB_index = new_Tindex;

	// generate the TID
	uint64_t TID = CreateTID(SCHEDULER->PCB[Pindex].PID, new_Tindex);

	// setup the TCB
	uint64_t fpu_address = (uint64_t)SCHEDULER->PCB[Pindex].TCB[new_Tindex].FPU;
	__asm__ __volatile__("fxsave (%%rax)"::"a" (fpu_address));
	SCHEDULER->PCB[Pindex].TCB[new_Tindex].CS = SCHEDULER->PCB[Pindex].TCB[0].CS;
	SCHEDULER->PCB[Pindex].TCB[new_Tindex].RIP = (uint64_t)start_address;
	SCHEDULER->PCB[Pindex].TCB[new_Tindex].RSP = (uint64_t)stack_ptr;
	SCHEDULER->PCB[Pindex].TCB[new_Tindex].RDI = (uint64_t)data;
	SCHEDULER->PCB[Pindex].TCB[new_Tindex].RFLAGS = SCHEDULER->PCB[Pindex].TCB[0].RFLAGS;
	SCHEDULER->PCB[Pindex].TCB[new_Tindex].RAX = 0;
	SCHEDULER->PCB[Pindex].TCB[new_Tindex].RBX = 0;
	SCHEDULER->PCB[Pindex].TCB[new_Tindex].RCX = 0;
	SCHEDULER->PCB[Pindex].TCB[new_Tindex].RDX = 0;
	SCHEDULER->PCB[Pindex].TCB[new_Tindex].RBP = 0;
	SCHEDULER->PCB[Pindex].TCB[new_Tindex].RSI = 0;
	SCHEDULER->PCB[Pindex].TCB[new_Tindex].R8 = 0;
	SCHEDULER->PCB[Pindex].TCB[new_Tindex].R9 = 0;
	SCHEDULER->PCB[Pindex].TCB[new_Tindex].R10 = 0;
	SCHEDULER->PCB[Pindex].TCB[new_Tindex].R11 = 0;
	SCHEDULER->PCB[Pindex].TCB[new_Tindex].R12 = 0;
	SCHEDULER->PCB[Pindex].TCB[new_Tindex].R13 = 0;
	SCHEDULER->PCB[Pindex].TCB[new_Tindex].R14 = 0;
	SCHEDULER->PCB[Pindex].TCB[new_Tindex].R15 = 0;
	SCHEDULER->PCB[Pindex].TCB[new_Tindex].TID = TID;

	// set the thread state to active
	pdb->thread_usage_map[new_Tindex] = THREAD_STATE_ACTIVE;

	// update the active thread count
	SCHEDULER->PCB[Pindex].active_threads++;

	// unlock the PCB
	BakeryLeave(&(SCHEDULER->PCB[Pindex].lock), GetCPUID());

	// find a CPU to move it to
	int CPU = GetCPUWithSmallestActiveQueue();

	// move it to that CPU
	MoveThreadToCPU(CPU, tlc);

	// increase system thread count
	BakeryEnter(&(thread_count_lock), GetCPUID());
	SCHEDULER->total_thread_count++;
	BakeryLeave(&(thread_count_lock), GetCPUID());

	// finished
	return TID;
}


/////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////
// top-level function to create a system process
// returns the PID of the created process
// assumes interrupts are disabled
static SCH_CLS process_init_lock;
uint64_t CreateSystemProc(uint64_t(*start_address)(void), void *stack_ptr, uint64_t param1, uint64_t param2, char *image_name, uint32_t image_length, char *description, uint32_t description_length)
{
	// lock the process init section
	BakeryEnter(&process_init_lock, GetCPUID());

	// PID container
	uint64_t PID = 0;

	// get a system PCB
	uint32_t PCB = 0;
	GetSystemPCB(&PCB);

	// check it
	if(PCB == 0)
	{
		return 0;
	}

	// generate the PID
	PID = CreatePID(PCB);

	// now set up the PCB
	SCHEDULER->PCB[PCB].owner_ID = 0; 	// system proc so owner = 0
	SCHEDULER->PCB[PCB].PID = PID;
	SCHEDULER->PCB[PCB].state = PROCESS_STATE_ACTIVE;

	// copy the description across
	if(description_length > 255)
	{
		description_length = 255;
	}

	int i;
	for(i = 0; i < description_length; i++)
	{
		SCHEDULER->PCB[PCB].process_description[i] = description[i];
	}
	SCHEDULER->PCB[PCB].process_description[description_length] = 0;

	// set up the PDB
	if(image_length > 255)
	{
		image_length = 255;
	}

	for(i = 0; i < image_length; i++)
	{
		SCHEDULER->PCB[PCB].PDB->image_name[i] = image_name[i];
	}
	SCHEDULER->PCB[PCB].PDB->image_name[image_length] = 0;

	for(i = 0; i < 256; i++)
	{
		SCHEDULER->PCB[PCB].PDB->thread_usage_map[i] = THREAD_STATE_INACTIVE;
	}

	SCHEDULER->PCB[PCB].PDB->working_directory[0] = 0;

	// increase system process count
	SCHEDULER->total_process_count++;

	// initialise the main thread
	SpawnMasterThread(PID, start_address, stack_ptr, 0x08, param1, param2);

	// finished
	BakeryLeave(&process_init_lock, GetCPUID());
	return PID;
}


/////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////
// lock entry/exits
extern void ACDMSpinEnter(uint64_t *lock);
extern void ACDMSpinLeave(uint64_t *lock);

// internal functions to remove a thread from the sleeping and waiting queues
static void RemoveFromSleeping(uint32_t Pindex, uint32_t Tindex, uint64_t PID)
{
	// search through sleeping queue
	// check if empty
	if(SCHEDULER->sleeping_start != NULL)
	{
		// queue is not empty
		// check the first entry
		int p = SCHEDULER->sleeping_start->PCB_index;
		int t = SCHEDULER->sleeping_start->TCB_index;
		if((p == Pindex) && (t == Tindex) && (PID == SCHEDULER->PCB[Pindex].PID))
		{
			// Pindex, Tindex and global PID matches. found the thread
			SCH_TLC *thread = SCHEDULER->sleeping_start;
			SCHEDULER->sleeping_start = thread->next_TLC;
			SCHEDULER->sleeping_start->sleep_offset += thread->sleep_offset;
			thread->next_TLC = NULL;

			// check if queue is now empty
			if(SCHEDULER->sleeping_start == NULL)
			{
				// queue is empty, update end pointer
				SCHEDULER->sleeping_end = NULL;
			}

			// thread was removed, update process's thread count and thread status
			SCHEDULER->PCB[Pindex].PDB->thread_usage_map[Tindex] = THREAD_STATE_INACTIVE;
			SCHEDULER->PCB[Pindex].active_threads--;
		}
		else
		{
			// it was not the first item
			SCH_TLC *prev_thread = SCHEDULER->sleeping_start;

			// iterate through until the thread is found or the end of the queue is found
			while(1)
			{
				// check for end of queue
				if(prev_thread->next_TLC == NULL)
				{
					// reached end of list
					break;
				}

				// check next thread values
				int p = prev_thread->next_TLC->PCB_index;
				int t = prev_thread->next_TLC->TCB_index;
				if((p == Pindex) && (t == Tindex) && (PID == SCHEDULER->PCB[Pindex].PID))
				{
					// Pindex, Tindex and global PID matches. found the thread
					SCH_TLC *thread = prev_thread->next_TLC;
					prev_thread->next_TLC = thread->next_TLC;
					prev_thread->next_TLC->sleep_offset += thread->sleep_offset;
					if(thread->next_TLC == NULL)
					{
						SCHEDULER->sleeping_end = prev_thread;
					}
					thread->next_TLC = NULL;

					// thread was removed, update process's thread count and thread status
					SCHEDULER->PCB[Pindex].PDB->thread_usage_map[Tindex] = THREAD_STATE_INACTIVE;
					SCHEDULER->PCB[Pindex].active_threads--;

					break;
				}

				// not this thread, get next one
				prev_thread = prev_thread->next_TLC;
			}
		}
	}

	// finished
	return;
}

static void RemoveFromWaiting(uint32_t Pindex, uint32_t Tindex, uint64_t PID)
{
	// check if queue is empty
	if(SCHEDULER->waiting_start != NULL)
	{
		// queue is not empty
		// check the first entry
		int p = SCHEDULER->waiting_start->PCB_index;
		int t = SCHEDULER->waiting_start->TCB_index;
		if((p == Pindex) && (t == Tindex) && (PID == SCHEDULER->PCB[Pindex].PID))
		{
			// Pindex, Tindex and global PID matches. found the thread
			SCH_TLC *thread = SCHEDULER->waiting_start;
			SCHEDULER->waiting_start = thread->next_TLC;
			thread->next_TLC = NULL;

			// check if queue is now empty
			if(SCHEDULER->waiting_start == NULL)
			{
				// queue is empty, update end pointer
				SCHEDULER->waiting_end = NULL;
			}

			// thread was removed, update process's thread count and thread status
			SCHEDULER->PCB[Pindex].PDB->thread_usage_map[Tindex] = THREAD_STATE_INACTIVE;
			SCHEDULER->PCB[Pindex].active_threads--;
		}
		else
		{
			// it was not the first item
			SCH_TLC *prev_thread = SCHEDULER->waiting_start;

			// iterate through until the thread is found or the end of the queue is found
			while(1)
			{
				// check for end of queue
				if(prev_thread->next_TLC == NULL)
				{
					// reached end of list
					break;
				}

				// check next thread values
				int p = prev_thread->next_TLC->PCB_index;
				int t = prev_thread->next_TLC->TCB_index;
				if((p == Pindex) && (t == Tindex) && (PID == SCHEDULER->PCB[Pindex].PID))
				{
					// Pindex, Tindex and global PID matches. found the thread
					SCH_TLC *thread = prev_thread->next_TLC;
					prev_thread->next_TLC = thread->next_TLC;
					if(thread->next_TLC == NULL)
					{
						SCHEDULER->waiting_end = prev_thread;
					}
					thread->next_TLC = NULL;

					// thread was removed, update process's thread count and thread status
					SCHEDULER->PCB[Pindex].PDB->thread_usage_map[Tindex] = THREAD_STATE_INACTIVE;
					SCHEDULER->PCB[Pindex].active_threads--;

					break;
				}

				// not this thread, get next one
				prev_thread = prev_thread->next_TLC;
			}
		}

	}

	// finished
	return;
}


// top-level function to kill the current process
static uint64_t kill_lock = 0;
void KillThisProcess(uint64_t exit_value)
{
	// get lock on this area
	ACDMSpinEnter(&kill_lock);

	// get current PID and TID
	uint64_t PID = __KLIB__RCGetPID();
	uint32_t p = PID & 0xffff;
	uint64_t this_TID = __KLIB__RCGetTID();
	uint32_t t = this_TID & 0xff;

	// get pointer to thread status block
	uint32_t volatile *threads = SCHEDULER->PCB[p].PDB->thread_usage_map;

	// keep attempting to terminate other threads until this is the only one remaining
	while(SCHEDULER->PCB[p].active_threads > 1)
	{

		// get locks on sleeping and waiting queues
		__asm__ __volatile__("":::"memory");
		__asm__ __volatile__("cli");
		__asm__ __volatile__("":::"memory");
		uint32_t CPU = GetCPUID();
		BakeryEnter(&(SCHEDULER->sleeping_lock), CPU);
		BakeryEnter(&(SCHEDULER->waiting_lock), CPU);
		BakeryEnter(&(SCHEDULER->PCB[p].lock), CPU);
		// iterate through each thread
		int i;
		for(i = 0; i < 256; i++)
		{
			if(i != t)
			{
				// is it sleeping?
				if(threads[i] == THREAD_STATE_SLEEPING)
				{
					// remove it from the sleeping queue
					RemoveFromSleeping(p, i, PID);
				}
				// is it waiting?
				else if(threads[i] == THREAD_STATE_WAITING)
				{
					// remove it from the waiting queue
					RemoveFromWaiting(p, i, PID);
				}
				// is it active
				else if(threads[i] == THREAD_STATE_ACTIVE)
				{
					// set it to terminating to force it to finish
					threads[i] = THREAD_STATE_TERMINATING;
				}
			}
		}

		// release locks on sleeping and waiting queues
		BakeryLeave(&(SCHEDULER->PCB[p].lock), CPU);
		BakeryLeave(&(SCHEDULER->waiting_lock), CPU);
		BakeryLeave(&(SCHEDULER->sleeping_lock), CPU);
		__asm__ __volatile__("":::"memory");
		__asm__ __volatile__("sti");
		__asm__ __volatile__("":::"memory");

		// wait a bit
		SleepMe(20000);
	}


	// release all resources but memory
	ACDMDestroyWindowsForProc(PID);
	DeallocateForServerPID(PID);

	// set process state to terminating & set this thread's return value
	__asm__ __volatile__("":::"memory");
	__asm__ __volatile__("cli");
	__asm__ __volatile__("":::"memory");
	uint32_t CPU = GetCPUID();
	BakeryEnter(&(SCHEDULER->PCB[p].lock), CPU);

	SCHEDULER->PCB[p].state = PROCESS_STATE_TERMINATING;
	SCHEDULER->PCB[p].TCB[t].close_return_value = exit_value;

	BakeryLeave(&(SCHEDULER->PCB[p].lock), CPU);
	__asm__ __volatile__("":::"memory");
	__asm__ __volatile__("sti");
	__asm__ __volatile__("":::"memory");


	// finished
	// release lock and enter scheduler
	ACDMSpinLeave(&kill_lock);
	__asm__ __volatile__("":::"memory");
	__asm__ __volatile__("int $50");
	__asm__ __volatile__("":::"memory");
	return;
}


// top-level function to kill the current thread
void KillThisThread(uint64_t return_value)
{
	// check if this is the only thread in the process
	// if so, terminate the process instead
	uint64_t PID = __KLIB__RCGetPID();
	uint32_t p = PID & 0xffff;
	if(SCHEDULER->PCB[p].active_threads == 1)
	{
		KillThisProcess(return_value);
	}

	// ootherwise just kill this thread
	// get CPU ID
	__asm__ __volatile__("":::"memory");
	__asm__ __volatile__("cli");
	__asm__ __volatile__("":::"memory");
	int CPU = GetCPUID();

	// get the Thread index and the Process index
	int Pindex = SCHEDULER->CPU_current_thread[CPU]->PCB_index;
	int Tindex = SCHEDULER->CPU_current_thread[CPU]->TCB_index;


	// disable interrupts and get a lock on the PCB for this thread
	BakeryEnter(&(SCHEDULER->PCB[Pindex].lock), GetCPUID());

	// save the thread's return value (for any thread that is waiting on it to close)
	SCHEDULER->PCB[Pindex].TCB[Tindex].close_return_value = return_value;

	// set the thread state to terminating
	SCHEDULER->PCB[Pindex].PDB->thread_usage_map[Tindex] = THREAD_STATE_TERMINATING;

	// release lock
	BakeryLeave(&(SCHEDULER->PCB[Pindex].lock), GetCPUID());

	// yield timeslice (this will play havock with the idle counter, but that's only an approximation, anyway...)
	__asm__ __volatile__("":::"memory");
	__asm__ __volatile__("int $50");
	__asm__ __volatile__("":::"memory");


	// reenable interrupts
	__asm__ __volatile__("":::"memory");
	__asm__ __volatile__("sti");
	__asm__ __volatile__("":::"memory");

	// finished
	return;
}

// top-level function to deallocate a PCB
void ReleasePCB(uint32_t pcb)
{
	// check if valid
	if(pcb < 1 || pcb >= 2048)
	{
		// invalid, return
		return;
	}

	// check if system or user
	if(pcb < 16)
	{
		// is a system PCB
		ReleaseSystemPCB(&(SCHEDULER->PCB[pcb]));
	}
	else
	{
		// is a user-mode PCB
		ReleasePCB(pcb);
	}

	// finished
	return;
}



















